/* transporte.c
 * Luiz Guilherme M. Massa, 10005312
 * Matheus Martins Alves, 10108835
 * Marcelo C. R. Lopes Junior, 10104156
 * Vitor Sousa Negro, 10068104
 */

#define JANELA_QUANTIDADE 2
#define JANELA_TAMANHO 512
#define QTDE_MAX_IPS 5
#define TIMEOUT 5


void* enviaSegmento(void);
void* recebeSegmento(void);
void* gbnSender(void);
void enviador(int socket, char mensagem[], int bytes);
int ack_recebe = 0, base_janela = 0;
int ack_envia = 0;
int qtde_buffers_preenchidos;
int posicao_janela;
int tamanho_mensagem;
int sinc_thread_transporte;
segmento janela[JANELA_QUANTIDADE];

pthread_t thread_transporte_id[4];



int qtde_ips = 0;

struct estrutura_ips {
    int statusIPS; //fechado (0) ou aberto (1)
    int IC; // numero unico Conexao
    int statusIC; //fechado (0) ou aberto (1)
} ips[QTDE_MAX_IPS];


int abrir_ponto_servico() {
    int i = 0;
    int posicao = -1;

    while(i < QTDE_MAX_IPS && posicao == -1) {
        if(ips[i].statusIPS == 0)
            posicao = i;
        i++;
    }

    if(posicao == -1)
        return -1;
        
    //seta veriaveis e status
    ips[posicao].statusIPS = 1; // aberto
    ips[posicao].IC = 0;
    ips[posicao].statusIC = 0; // fechado
    
    if(qtde_ips <= posicao)   
        qtde_ips++;
    
    return posicao;
}

int fechar_ponto_servico(int ponto_servico) {

    if(ponto_servico < 0 || ponto_servico >= QTDE_MAX_IPS)
        return;

    ips[ponto_servico].statusIPS = 0; // aberto
    ips[ponto_servico].IC = 0;
    ips[ponto_servico].statusIC = 0; // fechado

    return 1;
}





void* recebeSegmento(void)
{
    char mensagem[1000000];
    // pthread_mutex_lock(&sinc_recebe_transp_rede);
  
    ack_recebe = 0;

    while(1) {

        pthread_mutex_lock(&sinc_recebe_rede_transp);
        while(bufferRedeTransporte.preenchido != 1) {
            if(TESTE_SINC) printf("Transporte -> Não tem nada no buffer da rede, aguardando...\n");
            sleep(1);
            pthread_mutex_unlock(&sinc_recebe_rede_transp);
            usleep(1);
            pthread_mutex_lock(&sinc_recebe_rede_transp);
        }

        if(TESTE_SINC) printf("Transporte -> Recebi um segmento %c (offset: %d)!\n", bufferRedeTransporte.segmento.tipo, bufferRedeTransporte.segmento.ack);
  
        if(bufferRedeTransporte.segmento.tipo == 'S' && bufferRedeTransporte.segmento.ack == ack_recebe) {
            if(TESTE_SINC) printf("Transporte -> Recebi o segmento esperado, vou enviar um ack.\n");

            // Enviar ACK para quem me enviou o segmento
            bufferTransporteRede.no_dst = bufferRedeTransporte.segmento.no_src;
            bufferTransporteRede.preenchido = 1;
            bufferTransporteRede.segmento.tipo = 'A';
            bufferTransporteRede.segmento.ack = bufferRedeTransporte.segmento.ack;
            bufferTransporteRede.segmento.no_src = meuIndiceNo;

            // Junto a parte
            memcpy(mensagem + ack_recebe, &bufferRedeTransporte.segmento.data, 512);
            
            ack_recebe = ack_recebe + 512;

            if(ack_recebe >= bufferRedeTransporte.segmento.tamanho_msg)
                puts(mensagem);

            pthread_mutex_unlock(&sinc_envia_rede_transp);
            usleep(1);
            // pthread_mutex_lock(&sinc_recebe_rede_transp);
            
            if(TESTE_SINC) printf("Transporte -> Enviei um ACK do offset %d.\n", bufferRedeTransporte.segmento.ack);
        }


        if(bufferRedeTransporte.segmento.tipo == 'A' && base_janela <= bufferRedeTransporte.segmento.ack) {
            base_janela = bufferRedeTransporte.segmento.ack + JANELA_TAMANHO;
            // ack_envia_confirmado = bufferRedeTransporte.segmento.ack;
            if(TESTE_SINC) printf("Transporte -> Recebi o ACK até o %d.\n", bufferRedeTransporte.segmento.ack);
        }

        // CONSUMIR
        bufferRedeTransporte.preenchido = 0;

        pthread_mutex_unlock(&sinc_recebe_rede_transp);
        usleep(1);
    }
}




void* gbnSender(void) {
    int i, j;
    int tempo_ack;
    int aux;
    int indice_janela_enviar;

    int erro_envio = 1;


    pthread_mutex_lock(&sinc_envia_rede_transp);

    while(1) {

        ack_envia = 0;

        // ack_envia_confirmado = -512;

        // Aguarda a camada de rede ser iniciada
        pthread_mutex_lock(&sinc_gbn_sender);

        while(bufferAplicacaoTransporte.preenchido != 1) {
            if(TESTE_SINC) printf("Transporte -> Não tem nada no buffer da app, aguardando...\n");
            sleep(1);
            pthread_mutex_unlock(&sinc_gbn_sender);
            usleep(1);
            pthread_mutex_lock(&sinc_gbn_sender);
        }
        if(TESTE_SINC) printf("Transporte -> Colocar segmentos na janela.\n");

        while(base_janela <= tamanho_mensagem) //até haver pacotes no buffer de aplicação
        {
            aux = base_janela;
            if(TESTE_SINC) printf("aux(%d) <= tamanho_mensagem(%d)\n", aux, tamanho_mensagem);
                
            i = 0; // Quantas posições foi preenchidas

            // Preencher janela
            while(qtde_buffers_preenchidos < (aux + (JANELA_TAMANHO * JANELA_QUANTIDADE))) {
                memcpy(&janela[i].data, ((void*) &bufferAplicacaoTransporte.mensagem) +  qtde_buffers_preenchidos, 512);
                if(TESTE_SINC) printf("Transporte -> Buffer preenchido: %d.\n", qtde_buffers_preenchidos);
                qtde_buffers_preenchidos += JANELA_TAMANHO;
                i++;
            }
        
            if (i==0)
                indice_janela_enviar = 0;
            else
                indice_janela_enviar = JANELA_QUANTIDADE - i;

            // envia os pacotes com sincronia
            for(j = indice_janela_enviar; j < JANELA_QUANTIDADE; j++) {
                bufferTransporteRede.no_dst = bufferAplicacaoTransporte.no_dst;
                bufferTransporteRede.preenchido = 1;
                bufferTransporteRede.segmento.tipo = 'S';
                bufferTransporteRede.segmento.tamanho_msg = tamanho_mensagem;
                bufferTransporteRede.segmento.ack = aux + (j*512);
                bufferTransporteRede.segmento.no_src = bufferAplicacaoTransporte.no_src;
                memcpy(&bufferTransporteRede.segmento.data, &janela[j].data, sizeof(janela[j].data));

                if(bufferTransporteRede.segmento.ack == 512 && erro_envio==1)
                    erro_envio--;
                else
                {
                    if(TESTE_SINC) printf("Transporte -> Buffer preenchido, enviar janela %d com ack %d.\n", j, bufferTransporteRede.segmento.ack);
                    pthread_mutex_unlock(&sinc_envia_rede_transp);
                    usleep(10);
                    pthread_mutex_lock(&sinc_envia_rede_transp);
                    if(TESTE_SINC) printf("Transporte -> Fui liberado para enviar outro segmento, fechando sincronia envia.\n");
                }
            }

            //aguarda ack
            tempo_ack = 0;
            while(tempo_ack < 5) {
                usleep(500000);
                if(TESTE_SINC) printf("Transporte -> Esperando ACK (%d)...\n", tempo_ack);
                tempo_ack++;
            }

            if(aux == base_janela)
                if(TESTE_SINC) printf("Transporte -> Aconteceu TIMEOUT.\n");

        } // Fim do envio de janelas

        bufferAplicacaoTransporte.preenchido = 0;
        pthread_mutex_unlock(&sinc_envia_rede_transp);

    } // While infinito
}


 void iniciaTransporte(void) {

    if(TESTE_SINC) printf("Camada de transporte iniciada.\n");

    pthread_mutex_lock(&sinc_gbn_sender);
    
    if(pthread_create(&(thread_transporte_id[0]), NULL, &recebeSegmento) != 0) {
        perror("pthread_create()");
        exit(1);
    }

     if(pthread_create(&(thread_transporte_id[1]), NULL, &gbnSender) != 0) {
        perror("pthread_create()");
        exit(1);
    }

}



void enviador(int socket, char* mensagem, int bytes) {
    // Ele trava o sinc_gbn_sender

    tamanho_mensagem = bytes;
    qtde_buffers_preenchidos = 0;
    posicao_janela = 0;

    // Preencher o buffer
    memcpy(&bufferAplicacaoTransporte.mensagem, mensagem, bytes);
    bufferAplicacaoTransporte.preenchido = 1;
    bufferAplicacaoTransporte.no_dst = socket;
    bufferAplicacaoTransporte.no_src = meuIndiceNo;

    // Desbloquear o gbn
    if(TESTE_SINC) printf("Transporte -> Buffer preenchido, liberando sincronia gbn_sender.\n");
    pthread_mutex_unlock(&sinc_gbn_sender);
    sleep(1);
    pthread_mutex_lock(&sinc_gbn_sender);
    if(TESTE_SINC) printf("Transporte -> Fui liberado para a camada de app.\n");

    // Retornar alguma coisa


}
